home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PsL Monthly 1993 December
/
PSL Monthly Shareware CD-ROM (December 1993).iso
/
prgmming
/
dos
/
c
/
cjdates.exe
/
DATES.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-07-21
|
15KB
|
403 lines
%PAGESIZE 58,124
;*******************************************************************************
;
; ZDAY and ZDATE
;
; These are routines for converting Gregorian dates to and from Day Numbers.
;
; The routines are called using the PASCAL calling sequence, and are FAR calls.
; All pointers are FAR pointers.
;
; The routines are written to run on an 8088 so as to be compatible with all
; machines based on this architecture, and use the Borland "PASCAL" calling
; sequence so as to be callable from programs compiled with both Borland's C
; and Pascal compilers.
;
; The Borland Turbo Assembler (V2.0 or better) is required to assemble this code.
;
;===============================================================================
;
; The C calling sequences are defined by these prototypes:
;
; unsigned long int far pascal ZDay( unsigned int Year, unsigned int Month,
; unsigned int Day );
;
; int far pascal ZDate(unsigned long int DayNumber, unsigned int far *Year,
; unsigned int far *Month, unsigned int far *Day );
;
;-------------------------------------------------------------------------------
;
; The PASCAL prototypes (you will $L the object code from this assembly into
; a TPU) would be:
;
; function ZDay(Year, Month, Day : word) : longint;
;
; function ZDate(DayNumber : longint; var Year, Month, Day : word ) : boolean;
;
;-------------------------------------------------------------------------------
;
; ZDay returns a 32-bit unsigned integer representing the Day Number calculated
; from the supplied Gregorian date. Although the Pascal call defines it as a
; longint, you will not have to worry about getting a negative number back.
;
; If the resulting Day Number is zero, you have supplied a Gregorian date that
; is too early or too far in the future for the calculations to be performed
; correctly with this routine's methods.
;
; The Year, Month and Day parameters are all unsigned 16-bit integers, Year
; must be the FULL YEAR. 1902 must be supplied as 1902, not 02. It must be
; in the range 1 to 25599 or zero will be returned. Month and Day must be in
; the range 0-65535. These restrictions should not be too restrictive for most
; purposes.
;
; Note that Day, Month and Year are all UNSIGNED. When you are doing weird
; date calculations you must NEVER try to supply negative values for these.
; This is also true of the any Day Number you give to ZDate! Negative numbers
; look like large positive numbers and will give Wrong Results!
;
; The same parameters are used with ZDate, except no value is returned since it
; is a procedure (void function) rather than a function. Just supply the Day
; Number, and ZDate fills in the Year, Month and Day for you. The supplied Day
; Number should be greater than 121 and less than 23920640. For Day Numbers
; outside this range, ZDate returns a Gregorian date of 0-0-0 and a return
; value of 0 (FALSE in both Pascal and C). A good conversion returns 1 (TRUE
; in both C and Pascal).
;
; NOTE: ZDay can convert certain dates into Day Numbers that are outside the
; range that ZDate can convert back. These dates, however, involve years
; that are either zero or very, very large, and are well outside the range of
; dates that we are likely to be interested in.
;
; A call to ZDay uses 14 bytes of space on the stack; a call to ZDate uses 26.
;
;===============================================================================
;
; WHAT'S IT ALL FOR, ANYWAY?
;
; All dates, valid or not, convert to Day Numbers of some kind. All Day
; Numbers convert to valid Gregorian dates. So if you convert a Gregorian
; date to a Day Number and back, if the resulting Gregorian date doesn't match
; the original, the original was invalid.
;
; The Day Number is related to the Julian Day Number, but is not the same.
; It is valid only for the years since the Gregorian calendar was introduced.
;
; You can use the Day Numbers of two dates to find the number of days between
; them.
;
; The remainder resulting when the Day Number is divided by 7 is the day of the
; week, with 0 (Sunday) thru 6 (Saturday).
;
; To find the last day of a month, begin with the Gregorian date. Add 1 to
; the month (even to December), set the Day to 0, convert to a Day Number, then
; back to Gregorian.
;
; To find the Julian day of the year (different from the Julian Day Number!),
; convert the given Gregorian date to a Day Number. Then subtract from this
; the Day Number of January 0 (NOT 1!) of the same year.
;
; Convert a Julian Date to Gregorian by taking the Day Number of of January
; ZERO of the year. Add the Julian day of the year to this, then convert
; to a Gregorian date.
;
;*******************************************************************************
; (c) Copyright 1991 Crazy Jack
; All Rights Reserved
%NEWPAGE
IDEAL
MODEL LARGE
CODESEG
;
; The simplest routine converts the Gregorian date to a Day Number:
;
PROC PASCAL ZDAY FAR Year:WORD, Month:WORD, Day:WORD
PUBLIC ZDAY
MOV CX, [Year] ;Get Year.
MOV BX, [Month] ;Get Month.
CMP BX, 14 ;Month greater than 14 will give
JA FIXMNTH ;incorrect results.
MNTHOK:
CMP BL, 2 ;Is Month January or February?
JA NOADJ ;Jump if not,
OR CX, CX ;else be sure year isn't zero (we're
JZ DATEBAD ;in trouble if we decrement zero),
DEC CX ;then shift calculations to joint
ADD BX, 12 ;between February and March.
NOADJ:
CMP CH, 100 ;Any year too big to be divided by 100
JAE DATEBAD ;16-by-8-bit will cause divide overflow.
INC BX ;Need a little adjustment here---.
MOV AX, 43857 ;Calculate number of days due to months:
MUL BX ;First multiply by 306001, more than 16
SHL BX, 1 ;bits worth. The upper word is 4. We
SHL BX, 1 ;use shifts for speed. Earlier test
ADD DX, BX ;against 2141 ensures no overflow and
MOV BX, 10000 ;that we can divide by 10000.
DIV BX ;This gives INT(month * 30.6001).
MOV BX, AX ;Save the result (days due to months).
MOV AX, 365 ;Now for days due to years:
MUL CX ;Gives days in normal years.
ADD AX, BX ;Add in days due to months.
ADC DX, 0 ;(There will be no carry from this!)
PUSH AX ;We need the AX for another divide.
SHR CX, 1 ;Find number of extra days due to
SHR CX, 1 ;leap years (add a year for every 4).
MOV AX, CX ;Remove days for 400-year
MOV BL, 25 ;Gregorian rule:
DIV BL ;First remove leap year day for each
XOR AH, AH ;century---
SUB CX, AX ;Result will always be positive.
SHR AL, 1 ;Then add back a leap year day for
SHR AL, 1 ;each 400 years.
ADD AX, CX ;No carry will occur. Why?
POP BX
ADD AX, BX ;Add leap year days to days due to
ADC DX, 0 ;month and year.
ADD AX, [Day] ;Fold in day of the month.
ADC DX, 0
SUB AX, 1 ;Finally, adjust so remainder from
SBB DX, 0 ;divide by 7 gives day of week.
;Back to caller with Day Number
GONE: ;Day Number in DX:AX.
RET
;
FIXMNTH: ;Sigh. Month is too big to give valid
MOV AX, BX ;results, so we must reduce it. We put
XOR DX, DX ;this here since it won't happen often
MOV BX, 12 ;and we don't want to slow the main line.
DIV BX ;We convert it to years and months.
MOV BX, DX ;Remainder becomes new month.
ADD CX, AX ;Quotient is years. Add to given year.
JNC MNTHOK ;Back to conversion if still in range.
;
DATEBAD:
XOR AX, AX ;If something's wrong, we clear the
MOV DX, AX ;Day Number in DX:AX to zero
JMP GONE ;and clear out.
;
ENDP ZDAY
%NEWPAGE
;
; Converting a Day Number back to a Gregorian date is more complicated:
;
PROC PASCAL ZDATE FAR DayNumber:WORD:2, Year:FAR PTR WORD, Month:FAR PTR WORD, Day:FAR PTR WORD
PUBLIC ZDATE
PUSH SI
PUSH DI
MOV DX, [DayNumber+2] ;Get Day Number from caller.
CMP DX, 365 ;Bigger than this and we can't
JAE RELAY2 ;extract the year!
MOV AX, [DayNumber] ;Okay, get the rest of the Day Number.
MOV SI, AX ;We save a copy of it.
MOV DI, DX
SUB AX, 121 ;Fi